// `include "include/param.svh"
// `include "include/dtype.svh"
// `include "include/isa.svh"
// `include "design/lpu/lpu_intf.svh"
// `include "design/mxu/mxu_intf.svh"
// `include "design/mxu/mxu_param.svh"

module mxu (
    input logic                  clk,
    input logic                  rst_n,
    // Interface from LPU (Instruction input)
          lpu_is_mxu_instr_if.in lpu_mxu_instr,
    // Interfaces to external memory buffers
          lmb_rd_req_if.out      lmb_rd_req,
          lmb_dat_if.in          lmb_rd_dat,
          rmb_rd_req_if.out      rmb_rd_req,
          rmb_dat_if.in          rmb_rd_dat,
          pmb_rd_req_if.out      pmb_rd_req,
          pmb_dat_if.in          pmb_rd_dat,
    // Interface to ARU for PSB read
          psb_rd_req_if.in       aru_psb_rd_req,
          aru_psb_dat_if.out     aru_psb_rd_dat,
    // // PSB write interface (to external)
    //       psb_wr_req_if.out      psb_wr_req,
    // Done signal
          done_if.out            done
);

    // ========================================================================
    // Internal Interface Declarations
    // ========================================================================

    // Configuration interfaces from instruction manager to read generators
    mxu_cfg_if lmb_rdgen_cfg_if (
        .clk  (clk),
        .rst_n(rst_n)
    );

    mxu_cfg_if rmb_rdgen_cfg_if (
        .clk  (clk),
        .rst_n(rst_n)
    );

    mxu_cfg_if psb_rdgen_cfg_if (
        .clk  (clk),
        .rst_n(rst_n)
    );

    mxu_cfg_if pmb_rdgen_cfg_if (
        .clk  (clk),
        .rst_n(rst_n)
    );

    mxu_cfg_if mat_ctrl_cfg_if (
        .clk  (clk),
        .rst_n(rst_n)
    );

    // Data interfaces from read generators to mat_ctrl
    lmb_dat_if lmb_dat_to_ctrl_if (
        .clk  (clk),
        .rst_n(rst_n)
    );

    rmb_dat_if rmb_dat_to_ctrl_if (
        .clk  (clk),
        .rst_n(rst_n)
    );

    pmb_dat_if pmb_dat_to_ctrl_if (
        .clk  (clk),
        .rst_n(rst_n)
    );

    mxu_psb_dat_if psb_dat_to_ctrl_if (
        .clk  (clk),
        .rst_n(rst_n)
    );

    // PSB read request interface from psb_rdgen
    psb_rd_req_if mat_psb_rd_req_if (
        .clk  (clk),
        .rst_n(rst_n)
    );

    // PSB write request interface from psb
    psb_wr_req_if mat_psb_wr_req_if (
        .clk  (clk),
        .rst_n(rst_n)
    );

    // PSB read data interface from PSB to psb_rdgen
    mxu_psb_dat_if mat_psb_rd_dat_if (
        .clk  (clk),
        .rst_n(rst_n)
    );

    // Interface from mat_ctrl to mat_array
    mxu_mat_ctrl_mat_array_if mat_ctrl_to_array_if (
        .clk  (clk),
        .rst_n(rst_n)
    );

    // Interface from mat_array to psb_wrgen
    mxu_psb_dat_if mat_array_to_wrgen_if (
        .clk  (clk),
        .rst_n(rst_n)
    );

    // ========================================================================
    // Module Instantiations
    // ========================================================================

    // Instruction Manager
    mxu_instr_mngr u_mxu_instr_mngr (
        .clk          (clk),
        .rst_n        (rst_n),
        .lpu_mxu_instr(lpu_mxu_instr),
        .lmb_rdgen_cfg(lmb_rdgen_cfg_if.lmb_out),
        .rmb_rdgen_cfg(rmb_rdgen_cfg_if.rmb_out),
        .psb_rdgen_cfg(psb_rdgen_cfg_if.psb_out),
        .pmb_rdgen_cfg(pmb_rdgen_cfg_if.pmb_out),
        .mat_ctrl_cfg (mat_ctrl_cfg_if.mat_ctrl_out)
    );

    // LMB Read Generator
    mxu_lmb_rdgen u_mxu_lmb_rdgen (
        .clk       (clk),
        .rst_n     (rst_n),
        .mxu_cfg   (lmb_rdgen_cfg_if.lmb_in),
        .lmb_rd_req(lmb_rd_req)
        // .lmb_rd_dat    (lmb_rd_dat),
        // .lmb_dat_to_mat(lmb_dat_to_ctrl_if.out)
    );

    // RMB Read Generator
    mxu_rmb_rdgen u_mxu_rmb_rdgen (
        .clk           (clk),
        .rst_n         (rst_n),
        .mxu_cfg       (rmb_rdgen_cfg_if.rmb_in),
        .mxu_rmb_rd_req(rmb_rd_req)
        // .rmb_rd_dat    (rmb_rd_dat),
        // .rmb_dat_to_mat(rmb_dat_to_ctrl_if.out)
    );

    // PMB Read Generator
    mxu_pmb_rdgen u_mxu_pmb_rdgen (
        .clk           (clk),
        .rst_n         (rst_n),
        .mxu_cfg       (pmb_rdgen_cfg_if.pmb_in),
        .mxu_pmb_rd_req(pmb_rd_req)
        // .pmb_rd_dat    (pmb_rd_dat),
        // .pmb_dat_to_mat(pmb_dat_to_ctrl_if.out)
    );

    // PSB Read Generator
    mxu_psb_rdgen u_mxu_psb_rdgen (
        .clk           (clk),
        .rst_n         (rst_n),
        .mxu_cfg       (psb_rdgen_cfg_if.psb_in),
        .mxu_psb_rd_req(mat_psb_rd_req_if.out)
    );

    // PSB (Partial Sum Buffer)
    mxu_psb u_mxu_psb (
        .clk          (clk),
        .rst_n        (rst_n),
        .mat_rd_req_if(mat_psb_rd_req_if.in),
        .aru_rd_req_if(aru_psb_rd_req),
        .psb_wr_req_if(mat_psb_wr_req_if.in),
        .mat_rd_dat_if(mat_psb_rd_dat_if.out),
        .aru_rd_dat_if(aru_psb_rd_dat)
    );

    // Matrix Controller
    mxu_mat_ctrl u_mxu_mat_ctrl (
        .clk                        (clk),
        .rst_n                      (rst_n),
        .u_mxu_cfg_if               (mat_ctrl_cfg_if.mat_ctrl_in),
        .u_lmb_dat_if               (lmb_rd_dat),
        .u_rmb_dat_if               (rmb_rd_dat),
        .u_pmb_dat_if               (pmb_rd_dat),
        .u_psb_dat_if               (psb_dat_to_ctrl_if.in),
        .u_mxu_mat_ctrl_mat_array_if(mat_ctrl_to_array_if.mat_ctrl_out)
    );

    // Matrix Array (Systolic Array)
    mxu_mat_array u_mxu_mat_array (
        .clk                        (clk),
        .rst_n                      (rst_n),
        .u_mxu_mat_ctrl_mat_array_if(mat_ctrl_to_array_if.mat_ctrl_in),
        .mat_psb_dat                (mat_array_to_wrgen_if.out)
    );

    // PSB Write Generator
    mxu_psb_wrgen u_mxu_psb_wrgen (
        .clk        (clk),
        .rst_n      (rst_n),
        .mxu_cfg    (psb_rdgen_cfg_if.psb_in),   // Reuse PSB config
        .mat_psb_dat(mat_array_to_wrgen_if.in),
        .psb_wr_req (mat_psb_wr_req_if.out),
        .done       (done)
    );

    // Connect PSB read data from PSB to mat_ctrl
    assign psb_dat_to_ctrl_if.vld = mat_psb_rd_dat_if.vld;
    assign psb_dat_to_ctrl_if.dat = mat_psb_rd_dat_if.dat;
    assign mat_psb_rd_dat_if.rdy  = psb_dat_to_ctrl_if.rdy;

endmodule
